package fr.univ.lr.icone.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.LinkedBlockingQueue;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

import fr.univ.lr.icone.gui.event.Event;

/**
 * Class that implements a GUI (Graphic User Interface)
 * 
 * @author Elian ORIOU
 * 
 */

public class GraphicUI extends Thread implements IEventListener {

	/* Constants */
	private static final String AGENTS_NUMBER_STRING = "Agent Number = ";

	/* The event queue */
	private LinkedBlockingQueue<Event> eventQueue;
	private HashMap<Point, Integer> agents;
	private int agentNumber;
	private boolean active;

	/* Containers (Frame & Panels) */
	private JFrame frame;
	private JPanel rootPanel;
	private JPanel buttonsPanel;
	private JPanel informationPanel;

	/* Buttons */
	private JButton b_start;
	private JButton b_stop;
	private JButton b_quit;

	/* Layouts */
	private BorderLayout layout;
	private BoxLayout buttonsLayout;

	/* The drawing zone */
	private DrawingZone zone;

	/* Labels */
	private JLabel informationsTitle;
	private JLabel agentNumber_lbl;

	/* Buttons Listeners */
	private GUIEventControler controler;

	public GraphicUI() {
		this.eventQueue = new LinkedBlockingQueue<Event>();
		this.active = false;
		this.agents = new HashMap<Point, Integer>();
		this.agentNumber = agents.size();
	}

	/**
	 * Initializes the component
	 */

	private void init() {
		frame = new JFrame("Opinion Simulator 1.0");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	/**
	 * Builds the GUI with SWING components and manages layouts
	 */

	private void buildGUI() {
		/* Panels */
		rootPanel = new JPanel();
		buttonsPanel = new JPanel();

		/* Layouts */
		layout = new BorderLayout();
		rootPanel.setLayout(layout);
		buttonsLayout = new BoxLayout(buttonsPanel, BoxLayout.PAGE_AXIS);
		buttonsPanel.setLayout(buttonsLayout);

		/* Buttons */
		b_start = new JButton("Start Simulation");
		b_stop = new JButton("Stop Simulation");
		b_quit = new JButton("Quit");

		/* The Drawing zone */
		zone = new DrawingZone(40);
		zone.setSize(800, 800);

		/* The information panel */
		informationPanel = new JPanel();
		informationPanel.setBorder(BorderFactory.createLineBorder(Color.black));
		informationsTitle = new JLabel("Informations");
		informationPanel.add(informationsTitle);

		agentNumber_lbl = new JLabel(AGENTS_NUMBER_STRING + agentNumber);
		informationPanel.add(agentNumber_lbl);

		/* Adding element to panels */
		buttonsPanel.add(b_start);
		buttonsPanel.add(b_stop);
		buttonsPanel.add(b_quit);

		rootPanel.add(zone, BorderLayout.CENTER);
		rootPanel.add(buttonsPanel, BorderLayout.LINE_END);
		rootPanel.add(informationPanel, BorderLayout.PAGE_END);

		/* Fixes the rootPanel to the frame */
		frame.setContentPane(rootPanel);

		log("GUI built !");
	}

	/**
	 * Sets a listener on a component (to recover actions)
	 */

	private void setListeners() {
		controler = new GUIEventControler();
		b_start.addActionListener(controler);
		b_stop.addActionListener(controler);
		b_quit.addActionListener(controler);
	}

	@Override
	public void receiveEvent(Event e) {
		try {
			eventQueue.put(e);
			log("RECEIVES EVENT : " + e);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
	}

	@Override
	public void processEvent() throws Exception {
		Event e = eventQueue.poll();
		if (e == null) {
			return;
		}
		log("Processes Event : " + e);
		switch (e.getEventType()) {
		case AGENT_CREATION:
			if (e.getOpinion() == -1 || e.getPosition() == null) {
				throw new Exception("Missing opinion & position !");
			}
			zone.addAgent(e.getOpinion(), e.getPosition());
			/* Adding agent to the list */
			agents.put(e.getPosition(), e.getOpinion());
			agentNumber = agents.size();
			agentNumber_lbl.setText(AGENTS_NUMBER_STRING + agentNumber);
			log("Adding Agent at " + e.getPosition() + " with "
					+ e.getOpinion());
			break;
		case OPINION_CHANGED:
			if (e.getOpinion() == -1 || e.getPosition() == null) {
				throw new Exception("Missing opinion & position !");
			}
			log("Opinion changed for " + e.getPosition());
			zone.addAgent(e.getOpinion(), e.getPosition());
			break;
		case POSITION_CHANGED:
			if (e.getOldPosition() == null || e.getPosition() == null
					|| e.getOpinion() != -1) {
				throw new Exception("Missing oldposition, position & opinion !");
			}
			log("Position changed at " + e.getOldPosition() + " to "
					+ e.getPosition());
			zone.removeAgent(e.getOldPosition());
			zone.addAgent(e.getOpinion(), e.getPosition());
			break;
		case ENABLED_AGENT:
			/* Inutile pour le moment */
			break;
		case DISABLED_AGENT:
			/* Inutile pour le moment */
			break;
		}
	}

	@Override
	public LinkedBlockingQueue<Event> getEventQueue() {
		return eventQueue;
	}

	/**
	 * Thread execution method
	 */

	@Override
	public void run() {
		log("Running GUI thread");
		init();
		buildGUI();
		setListeners();
		frame.setBounds(new Rectangle(900, 800));
		frame.setLocationRelativeTo(null);
		frame.setResizable(false);
		frame.setVisible(true);

		while (true) {
			try {
				processEvent();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Inner class that processes all GUI events
	 * 
	 * @author Elian ORIOU
	 * 
	 */

	class GUIEventControler implements ActionListener {

		public GUIEventControler() {
		}

		/**
		 * Recovers actions from components
		 */

		@Override
		public void actionPerformed(ActionEvent e) {
			if (e.getSource().equals(b_start)) {
				// If the process is in pause mode, it will reboot
				if (active == false) {
					active = true;
				}
				// else
				try {
					zone.addAgent(4, new Point(10, 8));
					zone.addAgent(3, new Point(15, 18));
					agents.put(new Point(10, 8), 4);
					agents.put(new Point(15, 18), 3);
					agentNumber = agents.size();
					agentNumber_lbl.setText(AGENTS_NUMBER_STRING + agentNumber);
				} catch (Exception e1) {
					e1.printStackTrace();
				}
			} else if (e.getSource().equals(b_stop)) {
				// STOP PROCESS
				active = false;

			} else if (e.getSource().equals(b_quit)) {
				frame.dispose();
			}
		}
	}

	/**
	 * Log method
	 * 
	 * @param message
	 *            the message to log
	 */

	private void log(String message) {
		Date d = new Date();
		System.out.println("[LOG : " + d.toString() + "] :: " + message);
	}
}